Skip to content

Map.clear and Set.clear keep the bucket table - #100

Merged
ASDAlexander77 merged 1 commit into
mainfrom
map-clear-keeps-buckets
Nov 14, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
map-clear-keeps-buckets

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

What

Map.clear and Set.clear zero the bucket table in place and keep its length. They shrank it to length zero.

Why

Every get, has, set and delete computes hash % buckets.length. After a clear that was a remainder by zero:

  • the compiler does uint % uint in floating point (frem), so the result was NaN;
  • NaN was then converted to an array index (fptosi), which is undefined.

It gave right answers only by luck. On x86-64 the conversion yields index 0, shrinking an array zero-fills its storage and keeps the block, so index 0 read as an empty bucket; and the first insert after a clear resizes before it writes. Where the optimizer can see the length is zero it emits a trap instead: the same lookup on a plain emptied array crashed under the JIT.

Change

  • src/generics/lib.generics.ts: clear() in Map and in Set. The next insert still finds no room in the entries and resizes both tables, as before.
  • tests/map_clear_reuse.ts (new): lookups, delete and iteration on a cleared map and set, refilling past the first size, clear after deletes, clear twice.

For the reviewer

  • The new test passed before the change too. No wrong result could be produced on this platform, so it guards the behaviour and does not demonstrate the fault. The evidence for the fault is the generated IR and the trap on the plain-array probe.
  • A cleared map keeps its bucket table until the next insert, which replaces it with a small one.
  • Not changed here: uint % uint compiled as a floating-point remainder is in the compiler. It is on the path of every Map and Set operation.

Testing

  • Release, Windows x64: the whole suite passes for gc, rc, none and own, compiled and under the JIT.
  • Not run locally: debug builds, Linux, x86 and wasm.

🤖 Generated with Claude Code

clear() shrank the buckets to length zero. Every lookup after it took the hash
modulo that length: the compiler does the remainder in floating point, so it
was NaN, and the index made from NaN is undefined. On x86-64 it came out as 0,
and the emptied storage read as an empty bucket, so nothing showed; where the
optimizer can see the length is zero it emits a trap.

clear() now zeroes the buckets where they are. The next insert finds no room
in the entries and resizes both, as before.

map_clear_reuse.ts looks up, deletes from, walks and refills a cleared map and
set. It passed before the change too: it guards the behaviour, it did not
show the fault.

Tested in release on Windows x64 for gc, rc, none and own, compiled and under
the JIT; debug, Linux, x86 and wasm were not built locally.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 1d4bcf9 into main Nov 14, 2026
10 checks passed
@ASDAlexander77
ASDAlexander77 deleted the map-clear-keeps-buckets branch November 14, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant